#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#include "modbusdevice.h"

#include "iot_debug.h"
#include "dx_type.h"
#include "tools/auto_guard.h"
#include "phylink.h"
#include "modbustransfer.h"
#include "json_util.h"
#include "util_tools.h"
#include "base64.h"
#include "expparser.h"

#include "physicallinkmanager.h"
#include <logger.h>

ModbusDevice::ModbusDevice(uint16_t bay_id, uint16_t dev_id, IPhyLink *phy_dev) :
    IDevice(bay_id, dev_id, phy_dev),
    actived_(false),
    transfer_(nullptr),
    order_(0)
{
    recv_buf_ = new uint8_t[256];
}

ModbusDevice::~ModbusDevice()
{
    if (transfer_)
        delete transfer_;
    if (recv_buf_) {
        delete [] recv_buf_;
        recv_buf_ = nullptr;
    }
}


int ModbusDevice::genDevice(
        cJSON*          root,
        cJSON*          model_root,
        const char*     model_name,
        uint64_t        dev_addr,
        cJSON*          arr_params)
{
    int ret;

    if (model_root == nullptr ) {
        log_info("param error\n");
        return PARSE_MODE_FAIL;
    }

    dev_info_.model = model_name;
    dev_info_.addr  = u32toa(dev_addr&0xFF);
    dev_info_.protocolName = "Modbus";
    dev_info_.dev_id = u32toa(dev_id_);
    parseDevInfo(root);

    slave_addr_ = dev_addr&0xFF;
    transfer_ = new ModbusTransfer(phy_dev_);
    if (phy_dev_->comSettings()->is_uart) {
        bool crc16_h = false;
        cJSON_GetInt(model_root, "crc16_hl", crc16_h, arr_params);
        if (crc16_h)
            transfer_->setMode(ModbusTransfer::RTU_CRC_HL);
    }
    else {
        transfer_->setMode(ModbusTransfer::TCP);
    }

    yx_regs_.clear();
    ret = parseRegGroups(model_root, "YX", yx_regs_, arr_params);
    if (ret != true) {
        log_info("parse YX failed\n");
        goto error;
    }

    yc_regs_.clear();
    ret = parseRegGroups(model_root, "YC", yc_regs_, arr_params);
    if (ret != true) {
        log_info("parse YC failed\n");
        goto error;
    }

    yk_regs_.clear();
    ret = parseYKRegGroups(model_root, "YK", yk_regs_, arr_params);
    if (ret != true) {
        log_info("parse YK failed\n");
        goto error;
    }

    return true;
error:
    return ret;
}

bool ModbusDevice::isActived() const
{
    return actived_;
}

const RegBase *ModbusDevice::getYX(const char *reg_name)
{
    RegBase * reg = findReg(yx_regs_, reg_name);
    if (reg) {
        AUTO_GUARD(gd, MMutex, yx_mtx_);
        reg->changed();
    }
    return reg;
}

bool ModbusDevice::hasYX(const char *reg_name)
{
    return findReg(yx_regs_, reg_name) ? true : false;
}

bool ModbusDevice::setPreset(
                        const char*         app,
                        const char*         dev,
                        const char*         name,
                        const char*         reg_name,
                        const char*         uid,
                        const char*         type,
                        const int32_t       cmd,
                        const int32_t       action,
                        const int32_t       mode,
                        const int32_t       timeout)
{
    bool ret = false;
    if (!isActived())
        return false;

    yk_infos_mtx_.lock();
    for (YKRegGroups::iterator it=yk_regs_.begin(); it!=yk_regs_.end(); ++it)
    {
        YKRegGroup & group = *it;
        YKRegInfos::const_iterator it_infos = group.infos.begin();
        for (; it_infos!=group.infos.end(); ++it_infos)
        {
            if (strcmp(reg_name, it_infos->name.c_str()) == 0)
            {
                Action* info = NULL;
                if (yk_infos_.find(it_infos->name) == yk_infos_.end())
                {
                    info = new Action();
                    info->timestamp = 0;
                    info->step = Action::IDLE;
                    yk_infos_[it_infos->name] = info;
                }
                else
                {
                    info = yk_infos_[it_infos->name];
                }

                if (action == 0 && info->uid == uid && info->step != Action::CTRL && info->step != Action::WAIT_CTRL && info->step != Action::REPORT)
                {
                    info->timestamp = 0;
                    info->step = Action::IDLE;
                }
                else if (action == 2)
                {
                    if ((info->step == Action::IDLE) ||
                        (info->step == Action::PRESET && ((info->timestamp + (180*1000)) < getMilliseconds())))
                    {
                        info->timestamp  = getMilliseconds();
                        info->app        = app;
                        info->dev        = dev;
                        info->uid        = uid;
                        info->name       = name;
                        info->regname    = reg_name;
                        info->type       = type;
                        info->cmd        = cmd;
                        info->action     = action;
                        info->mode       = mode;
                        info->timeout    = timeout;
                        info->step       = Action::PRESET;
                    }
                    else if (uid == info->uid && info->step == Action::PRESET)
                    {
                        info->timestamp = 0;
                        info->step = Action::IDLE;
                        goto PREPARE_END;
                    }
                    else
                    {
                        goto PREPARE_END;
                    }
                }
                else
                {
                    goto PREPARE_END;
                }
                ret = true;
                goto PREPARE_END;
            }
        }
    }
PREPARE_END:
    yk_infos_mtx_.unlock();
    return ret;
}

bool ModbusDevice::setYK(
                    const char*         app,
                    const char*         dev,
                    const char*         name,
                    const char*         reg_name,
                    const char*         uid,
                    const char*         type,
                    const int32_t       cmd,
                    const int32_t       action,
                    const int32_t       mode,
                    const int32_t       timeout)
{
    bool ret = false;

    yk_infos_mtx_.lock();
    for (YKRegGroups::iterator it = yk_regs_.begin(); it != yk_regs_.end(); ++it)
    {
        YKRegGroup& group = *it;
        YKRegInfos::const_iterator it_infos = group.infos.begin();
        for (; it_infos!=group.infos.end(); ++it_infos)
        {
            if (strcmp(reg_name, it_infos->name.c_str()) == 0)
            {
                Action* info = NULL;
                if (yk_infos_.find(it_infos->name) == yk_infos_.end())
                {
                    info = new Action();
                    info->timestamp = 0;
                    info->step = Action::IDLE;
                    yk_infos_[it_infos->name] = info;
                }
                else
                {
                    info = yk_infos_[it_infos->name];
                }

                if ((action        ==              1)  &&
                    (info->step    == Action::PRESET)  &&
                    (info->app     ==            app)  &&
                    (info->dev     ==            dev)  &&
                    (info->uid     ==            uid)  &&
                    (info->name    ==           name)  &&
                    (info->regname ==       reg_name)  &&
                    (info->type    ==           type)  &&
                    (info->cmd     ==            cmd)  &&
                    (info->mode    ==           mode)  &&
                    ((info->timestamp + (180*1000)) > getMilliseconds()))
                {
                    info->step = Action::WAIT_CTRL;
                }
                else
                {
                    goto YK_END;
                }
                ret = true;
                goto YK_END;
            }
        }
    }
YK_END:
    yk_infos_mtx_.unlock();
    return ret;
}

bool ModbusDevice::setYK(const char *reg_name, uint32_t status)
{
    if (!isActived())
        return false;
    return _setYK(reg_name, status) == ERR_NO_ERROR ? true : false;
}

bool ModbusDevice::hasYK(const char *reg_name)
{
    for (YKRegGroups::iterator it=yk_regs_.begin(); it!=yk_regs_.end(); it++)
    {
        const YKRegGroup & group = *it;
        YKRegInfos::const_iterator it_infos=group.infos.begin();
        for (; it_infos!=group.infos.end(); it_infos++)
        {
            if (strcmp(reg_name, it_infos->name.c_str()) == 0)
            {
                return true;
            }
        }
    }
    return false;
}

const RegBase *ModbusDevice::getYC(const char *reg_name)
{
    RegBase * reg = findReg(yc_regs_, reg_name);
    if (reg) {
        AUTO_GUARD(gd, MMutex, yc_mtx_);
        reg->changed();
    }
    return reg;
}

bool ModbusDevice::hasYC(const char *reg_name)
{
    return findReg(yc_regs_, reg_name) ? true : false;
}

bool ModbusDevice::setTime(const DateTime_t &/*tm*/)
{
    return true;
}

int ModbusDevice::updateGatherTime(void* group)
{
    if (group)
    {
        for (RegGroups::iterator it = yx_regs_.begin(); it != yx_regs_.end(); ++it)
        {
            RegGroup* _group = *(it);
            if (_group == group)
            {
                _group->infos.tm = 0;
            }
        }
        for (RegGroups::iterator it = yc_regs_.begin(); it != yc_regs_.end(); ++it)
        {
            RegGroup* _group = *(it);
            if (_group == group)
            {
                _group->infos.tm = 0;
            }
        }
    }
    return 0;
}

int ModbusDevice::updateReg()
{
    if (!actived_) {
        if (phy_dev_->isActived())
            phy_dev_->disconnect();

        phy_dev_->connect(3000);
        if (!phy_dev_->isActived())
        {
            //如果串口无法打开.那么所有的数据项均为无效
            clearRegs();
            return ERR_LINK_NOT_OPEN;
        }
    }

    time_t tm = getCurrentTime();
    for (RegGroups::iterator it = yx_regs_.begin(); it != yx_regs_.end(); ++it)
    {
        RegGroup * group = *(it);
        if (group->infos.polling_cycle)
        {
            if (!group->infos.check(tm))
            {
                continue;
            }
            else
            {
                group->infos.tm = tm;
            }
        }

        int err = updateRegGroup(*group, yx_mtx_);
        if (err != ERR_NO_ERROR)
        {
            for (RegInfos::const_iterator info = group->infos.begin(); info != group->infos.end(); ++info)
            {
                RegBase *reg = *info;
                reg->setStatus(RegBase::INVALID);
            }
        }

        if (phy_dev_->comSettings()->cmd_interval_ms)
            mSleep(phy_dev_->comSettings()->cmd_interval_ms);
    }

    for (RegGroups::iterator it = yc_regs_.begin(); it != yc_regs_.end(); ++it)
    {
        RegGroup* group = *(it);
        if (group->infos.polling_cycle)
        {
            if (!group->infos.check(tm))
            {
                continue;
            }
            else
            {
                group->infos.tm = tm;
            }
        }

        int err = updateRegGroup(*group, yc_mtx_);
        if (err != ERR_NO_ERROR)
        {
            for (RegInfos::const_iterator info = group->infos.begin(); info != group->infos.end(); ++info)
            {
                RegBase *reg = *info;
                reg->setStatus(RegBase::INVALID);
            }
        }

        if (phy_dev_->comSettings()->cmd_interval_ms)
            mSleep(phy_dev_->comSettings()->cmd_interval_ms);
    }

    bool online = false;
    for (RegGroups::iterator it = yx_regs_.begin(); it != yx_regs_.end(); ++it)
    {
        RegGroup* group = *(it);
        for (RegInfos::const_iterator info = group->infos.begin(); info != group->infos.end(); ++info)
        {
            RegBase *reg = *info;
            if (reg->actived())
            {
                online = true;
                goto CHECK_DONE;
            }
        }
    }
    for (RegGroups::iterator it = yc_regs_.begin(); it != yc_regs_.end(); ++it)
    {
        RegGroup* group = *(it);
        for (RegInfos::const_iterator info = group->infos.begin(); info != group->infos.end(); ++info)
        {
            RegBase *reg = *info;
            if (reg->actived())
            {
                online = true;
                goto CHECK_DONE;
            }
        }
    }
CHECK_DONE:
    if (online == false)
    {
        clearRegs();
        actived_ = false;
        return ERR_LINK_NOT_OPEN;
    }
    else
    {
        actived_ = true;
    }

    return ERR_NO_ERROR;
}

int ModbusDevice::actions()
{
    yk_infos_mtx_.lock();
    //执行遥控
    for (std::map<std::string, Action*>::iterator it = yk_infos_.begin();
         it != yk_infos_.end();
         ++it)
    {
        switch (it->second->step)
        {
            case Action::WAIT_CTRL:
                it->second->status = (_setYK(it->second->regname.c_str(), it->second->cmd) == ERR_NO_ERROR) ? (true) : (false);
                it->second->step = Action::REPORT;
                break;
            case Action::CTRL:
            case Action::PRESET:
            case Action::IDLE:
            default:
                break;
        }
    }
    yk_infos_mtx_.unlock();
    return ERR_NO_ERROR;
}

void ModbusDevice::dumpYX()
{
    AUTO_GUARD(gd, MMutex, yx_mtx_);
    dumpData(yx_regs_);
}

void ModbusDevice::dumpYC()
{
    AUTO_GUARD(gd, MMutex, yc_mtx_);
    dumpData(yc_regs_);
}

void ModbusDevice::clearRegs()
{
    yx_mtx_.lock();
    for (RegGroups::const_iterator it=yx_regs_.begin(); it!= yx_regs_.end(); ++it)
    {
        RegGroup* group = *(it);
        for (RegInfos::const_iterator inf=group->infos.begin(); inf!=group->infos.end(); ++inf)
        {
            RegBase *reg = *inf;
            reg->setStatus(RegBase::INVALID);
        }
    }
    yx_mtx_.unlock();

    yc_mtx_.lock();
    for (RegGroups::const_iterator it=yc_regs_.begin(); it!= yc_regs_.end(); it++)
    {
        RegGroup* group = *(it);
        for (RegInfos::const_iterator inf=group->infos.begin(); inf!=group->infos.end(); ++inf)
        {
            RegBase *reg = *inf;
            reg->setStatus(RegBase::INVALID);
        }
    }
    yc_mtx_.unlock();
}

void ModbusDevice::getAllRegs(IDevice::Datas &regs) const
{
    regs.clear();
    for (auto it=yx_regs_.begin(); it!=yx_regs_.end(); ++it)
    {
        RegGroup* group = *(it);
        for (auto r = group->infos.begin(); r != group->infos.end(); ++r)
            regs.push_back(*r);
    }

    for (auto it=yc_regs_.begin(); it!=yc_regs_.end(); ++it)
    {
        RegGroup* group = *(it);
        for (auto r=group->infos.begin(); r != group->infos.end(); ++r)
            regs.push_back(*r);
    }
}

void ModbusDevice::getYX(IDevice::Datas &datas, bool force)
{
    datas.clear();
    AUTO_GUARD(gd, MMutex, yx_mtx_);
    for (RegGroups::const_iterator it=yx_regs_.begin(); it!= yx_regs_.end(); ++it)
    {
        RegGroup* group = *(it);
        for (RegInfos::const_iterator inf = group->infos.begin(); inf != group->infos.end(); ++inf)
        {
            RegBase *reg = *inf;
            if (reg->changed(force))
                datas.push_back(reg);
        }
    }
}

void ModbusDevice::getYC(IDevice::Datas &datas, bool force)
{
    datas.clear();
    AUTO_GUARD(gd, MMutex, yc_mtx_);
    for (RegGroups::const_iterator it=yc_regs_.begin(); it!= yc_regs_.end(); ++it)
    {
        RegGroup* group = *(it);
        for (RegInfos::const_iterator inf = group->infos.begin(); inf != group->infos.end(); ++inf)
        {
            RegBase *reg = *inf;
            if (reg->changed(force))
                datas.push_back(reg);
        }
    }
}

int ModbusDevice::updateRegGroup(ModbusDevice::RegGroup &group, MMutex &mtx)
{
    if (phy_dev_->comSettings()->dev_name.find(":36072") != std::string::npos)
    {
        int     i;
        uint8_t RecvBuf = 0;
        int     fd;
        char    val = 0;

        const char *DevName[4] =
        {
            "/dev/di1",
            "/dev/di2",
            "/dev/di3",
            "/dev/di4"
        };

        for(i = 0; i < 4; i++)
        {
            fd = open(DevName[i], O_RDWR);
            if(fd < 0)
            {
                return -1;
            }

            //DI 读数据
            read(fd, &val, 1);
            RecvBuf |= val << i;

            close(fd);
        }

        group.infos.fillData(&RecvBuf, 4, this, phy_dev_->comSettings()->dump_msg);

        return 0;
    }
    else
    {
        int err = sendCmdAndRecv(group.modbus, recv_buf_);
        if (ERR_NO_ERROR != err)
            return err;

        AUTO_GUARD(gd, MMutex, mtx);
        uint8_t len = 0;
        if (phy_dev_->comSettings()->is_uart)
        {
            len = group.modbus.return_size-group.modbus.data_pos-2;
        }
        else
        {
            len = group.modbus.return_size-group.modbus.data_pos;
        }

        group.infos.fillData(recv_buf_+group.modbus.data_pos, len, this, phy_dev_->comSettings()->dump_msg);

        return err;
    }
}

int ModbusDevice::_setYK(const char *reg_name, uint32_t status)
{
    AUTO_GUARD(gd, MMutex, yk_mtx_);
    for (YKRegGroups::iterator it=yk_regs_.begin(); it!=yk_regs_.end(); it++) {
        const YKRegGroup & group = *it;

        uint8_t item = 0;
        YKRegInfos::const_iterator it_infos=group.infos.begin();
        for (; it_infos!=group.infos.end(); it_infos++, item++)
        {
            if (strcmp(reg_name, it_infos->name.c_str()) == 0) {
                break;
            }
        }

        if (it_infos != group.infos.end())
        {
            if (phy_dev_->comSettings()->dev_name.find(":36072") != std::string::npos)
            {
                int     index;
                int     fd;
                int     ret = -1;

                const char *DevName[2] =
                {
                    "/dev/do1",
                    "/dev/do2"
                };

                if (strcmp(reg_name, "DO1") == 0)
                {
                    index = 0;
                }
                else if (strcmp(reg_name, "DO2") == 0)
                {
                    index = 1;
                }
                else
                {
                    log_info("YK name not DO1 or DO2!\n");
                    return -1;
                }

                fd = open(DevName[index], O_RDWR);
                if(fd < 0)
                {
                    return -1;
                }

                //DO 写
                ret = write(fd, &status, 1);
                if(ret < 0)
                {
                    printf("write value error!\n");
                    close(fd);
                    return -1;
                }
                close(fd);
                return 0;
            }
            else
            {
                ModbusYKCmdInfo cmd;
                cmd.dev_addr = slave_addr_;
                cmd.func_code = group.modbus.func_code;
                cmd.status = status ? it_infos->on : it_infos->off;
                cmd.reg_addr = group.modbus.addr+item;

                if (transfer_->isTcp())
                    cmd.order = order_++;

                return transfer_->setYKCmd(cmd);
            }
        }
    }

    return -1;
}

bool ModbusDevice::getTime(DateTime_t &/*tm*/)
{
    return true;
}

int ModbusDevice::parseYKRegInfo(cJSON *root, ModbusDevice::YKRegGroup &group)
{
    int ret = 0;
    std::string onval;
    std::string offval;

    cJSON *js = cJSON_GetObjectItem(root, "Regs");
    if (js && js->type == cJSON_Array)
    {
        int item = 0;
        do {
            cJSON* arrItem = cJSON_GetArrayItem(js, item++);
            if (!arrItem)
                break;

            if (arrItem)
            {
                YKRegInfo reg_inf;
                reg_inf.on  = 0xff00;
                reg_inf.off = 0;
                reg_inf.rate = 1.0;
                reg_inf.b = 0.0;
                reg_inf.type = TYPE_UINT16;

                std::string val_type;
                if (cJSON_GetString(arrItem, "Type", val_type))
                {
                    reg_inf.type = StringToDxType(val_type.c_str());

                    if (reg_inf.type == TYPE_UNKNOWN)
                    {
                        ret = MODE_TYPE_ERROR;
                        goto error;
                    }
                }

                cJSON * js_name = cJSON_GetObjectItem(arrItem, "RegName");
                std::vector<std::string> reg_names = parseStringObject(js_name);
                if (reg_names.size() == 0)
                {
                    ret = MODE_REGNAME_ERROR;
                    goto error;
                }

                cJSON_GetFloat(arrItem, "Rate", reg_inf.rate);
                cJSON_GetFloat(arrItem, "b", reg_inf.b);

                cJSON *js_cmd = cJSON_GetObjectItem(arrItem, "cmd");
                if (js_cmd && js_cmd->type == cJSON_Object)
                {
                    if (cJSON_GetString(js_cmd, "0", offval))
                    {
                        std::istringstream ss1(offval);
                        ss1 >> std::hex >> reg_inf.off;
                    }
                    if (cJSON_GetString(js_cmd, "1", onval))
                    {
                        std::istringstream ss2(onval);
                        ss2 >> std::hex >> reg_inf.on;
                    }
                }
                for (auto it=reg_names.begin(); it != reg_names.end(); ++it)
                {
                    reg_inf.name = *it;
                    group.infos.push_back(reg_inf);
                }

                //需要关联采集的数据
                cJSON * link_regs = cJSON_GetObjectItem(arrItem, "LinkReg");
                cJSON* link_reg = NULL;
                bool found(false);
                if (link_regs && link_regs->type == cJSON_Array)
                {
                    int i = 0;
                    do
                    {
                        link_reg = cJSON_GetArrayItem(link_regs, i++);
                        if (link_reg && link_reg->type == cJSON_String)
                        {
                            group.linkRegs.push_back(std::string(link_reg->valuestring));
                        }
                    } while (link_reg);
                }
            }
        } while(true);
    }

    return true;
error:
    log_info("parse failed\n");
    return ret;
}

int ModbusDevice::parseRegGroup(cJSON *root, RegGroup &group, cJSON *arr_params)
{
    int ret = 0;

    if (!parseModbusParam(root, group.modbus, arr_params))
    {
        ret = PARSE_MODE_FAIL;
        goto error;
    }

    group.infos.clear();

    ret = ::parseRegs(root, group.infos);

    if (ret != true)
    {
        goto error;
    }

    if (group.infos.shell.empty())
    {
        uint16_t num=0;
        int bit = 0;
        for (RegInfos::iterator it=group.infos.begin(); it!=group.infos.end(); it++) {
            RegBase * reg = *it;
            if (reg->rawDataType() == TYPE_BIT1) {
                bit++;
                if (bit == 8) {
                    bit = 0;
                    num++;
                }
            }
            else {
                if (bit)
                    num++;
                bit = 0;
                num += DxTypeSize(reg->rawDataType());
            }
        }

        if (num > group.modbus.reg_num*2) {
            log_info("Regs.size(%d) is over RegNum(%d) model_file:%s\n",
                    num, group.modbus.reg_num*2, model_name_.c_str());

            ret = MODE_REGSIZE_ERROR;
            goto error;
        }
    }

    return true;
error:
    log_info("parse failed\n");
    return ret;
}

int ModbusDevice::parseYKRegGroup(cJSON *root, ModbusDevice::YKRegGroup &group, cJSON *arr_params)
{
    int ret = 0;

    if (!parseModbusParam(root, group.modbus, arr_params))
    {
        ret = PARSE_MODE_FAIL;
        goto error;
    }

    group.infos.clear();
    ret = parseYKRegInfo(root, group);
    if (ret != true)
        goto error;

    return true;
error:
    log_info("parse failed\n");
    return false;
}

int ModbusDevice::parseRegGroups(cJSON *root,
                                 const char *block_name,
                                 RegGroups &reg_infos,
                                 cJSON *arr_params)
{
    int ret = 0;

    cJSON *js = cJSON_GetObjectItem(root, block_name);
    if (!js) return true;

    if (js->type == cJSON_Array) {
        int item = 0;
        do {
            cJSON* arrItem = cJSON_GetArrayItem(js, item);
            if (arrItem) {
                RegGroup* regs = new RegGroup();
                ret = parseRegGroup(arrItem, *regs, arr_params);
                if (ret == true) {
                    reg_infos.push_back(regs);
                }
                else {
                    goto error;
                }
                item++;
            }
            else
                break;
        } while(true);
    }

    return true;
error:
    log_info("parse %s failed\n", block_name);
    return ret;
}

int ModbusDevice::parseYKRegGroups(cJSON *root,
                                    const char *block_name,
                                    YKRegGroups &reg_infos,
                                    cJSON *arr_params)
{
    int ret = 0;
    cJSON *js = cJSON_GetObjectItem(root, block_name);
    if (!js) return true;

    if (js->type == cJSON_Array) {
        int item = 0;
        do {
            cJSON* arrItem = cJSON_GetArrayItem(js, item);
            if (arrItem) {
                YKRegGroup regs;
                ret = parseYKRegGroup(arrItem, regs, arr_params);
                if (ret == true) {
                    reg_infos.push_back(regs);
                }
                else {
                    goto error;
                }
                item++;
            }
            else
                break;
        } while(true);
    }

    return true;
error:
    log_info("parse %s failed\n", block_name);
    return ret;
}

bool ModbusDevice::parseModbusParam(cJSON *root, ModbusReg &modbus, cJSON *arr_params)
{
    std::string RegStartAddrPLC;

    if (!cJSON_GetInt(root, "FunCode", modbus.func_code)) {
        log_info("parse FunCode failed\n");
        return false;
    }

    if (cJSON_GetString(root, "RegStartAddrPLC", RegStartAddrPLC))
    {
        RegStartAddrPLC.replace(0, 3, i32toa(WhiteIdx));
        modbus.addr = atoi(ExpParser::calcExp(RegStartAddrPLC).c_str());
    }
    else if (!cJSON_GetInt(root, "RegStartAddr", modbus.addr, arr_params))
    {
        log_info("parse RegStartAddr failed\n");
        return false;
    }
    if (!cJSON_GetInt(root, "RegNum", modbus.reg_num)) {
        log_info("parse RegNum failed\n");
        return false;
    }
    if (!cJSON_GetInt(root, "DataPos", modbus.data_pos) ||
        !cJSON_GetInt(root, "ReturnSize", modbus.return_size))
    {
        modbus.calcDataPosAndReturnSize(transfer_->isTcp());
    }

    return true;
}

int ModbusDevice::sendCmdAndRecv(const ModbusReg &reg, uint8_t *recv_buf)
{
    ModbusCmdInfo cmd;
    cmd.dev_addr = slave_addr_;
    cmd.func_code = reg.func_code;
    cmd.reg_addr = reg.addr;
    cmd.reg_num = reg.reg_num;
    cmd.expect_recv_size = reg.return_size;

    if (transfer_->isTcp())
        cmd.order = order_++;

    return transfer_->sendCmdAndRecv(cmd, recv_buf);
}

std::vector<std::string> ModbusDevice::parseStringObject(cJSON *js)
{
    return ::parseStringObject(js);
}

void ModbusDevice::dumpData(const RegGroups &regs)
{
    bool print = false;

    for (RegGroups::const_iterator it = regs.begin(); it!= regs.end(); ++it)
    {
        RegGroup* group = *(it);
        dumpRegData(group->infos);
        print = true;
    }

    if (print)
        printf("\n");
}

RegBase *ModbusDevice::findReg(ModbusDevice::RegGroups &regs, const std::string &name)
{
    for (RegGroups::iterator it=regs.begin(); it!= regs.end(); ++it)
    {
        RegGroup* group = *(it);
        RegBase *reg = ::findReg(group->infos, name);
        if (reg)
            return reg;
    }
    return nullptr;
}

bool ModbusDevice::ModbusReg::calcDataPosAndReturnSize(bool tcp)
{
    switch (func_code) {
    case 3:
    case 4:
        if (tcp) {
            data_pos = 9;
            return_size = static_cast<uint8_t>(reg_num*2+data_pos);
        }
        else {
            data_pos = 3;
            return_size = static_cast<uint8_t>(reg_num*2+data_pos+2);
        }
        break;
    case 1:
    case 2:
        if (tcp) {
            data_pos = 9;
            return_size = static_cast<uint8_t>((reg_num+7)/8+data_pos);
        }
        else {
            data_pos = 3;
            return_size = static_cast<uint8_t>((reg_num+7)/8+data_pos+2);
        }
        break;
    default:
        return false;
    }

    return true;
}